/* Copyright (c) 2007 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is confidential property of 
 * Nordic Semiconductor. The use, copying, transfer or disclosure 
 * of such information is prohibited except by express written
 * agreement with Nordic Semiconductor.
 */

/** @file
Implementation of USB interface and callback functions.

This file contains functions that and an application need to set up and use
the USB interface. It also contains implementations of the callback functions
demanded by hal_usb.
*/

/**@{
 *@ingroup nordic_usb
 */

#include <reg24lu1.h>

#include "hal_nrf.h"
#include "cklf.h"
#include "cpu.h"
#include "hal_usb.h"
#include "hal_usb_hid.h"
#include "usb_api.h"
#include "nordic_common.h"

static hal_usb_dev_req_resp_t device_req_cb(hal_usb_device_req* req, uint8_t** data_ptr, uint16_t* size) reentrant;
static void suspend_cb(uint8_t allow_remote_wu) reentrant;
static void resume_cb() reentrant;
static void reset_cb() reentrant;
static uint8_t ep_1_in_cb(uint8_t *adr_ptr, uint8_t* size) reentrant;
static uint8_t ep_2_in_cb(uint8_t *adr_ptr, uint8_t* size) reentrant;

bool ep1_sent, ep2_sent;
volatile usb_state_t usb_state;

void usb_init(void)
{
  hal_usb_init(true, device_req_cb, reset_cb, resume_cb, suspend_cb);
   
  hal_usb_endpoint_config(0x81, 32, ep_1_in_cb);
  hal_usb_endpoint_config(0x82, 32, ep_2_in_cb);

  ep1_sent = true;
  ep2_sent = true;
  usb_state = USB_AWAKE;
}

void usb_wakeup(void)
{
  hal_usb_wakeup();
  usb_state = USB_AWAKE;
}

usb_state_t usb_get_state()
{
  return usb_state;
}

void usb_wait_for_configuration(void)
{
  volatile hal_usb_state_t usb_hal_state;
  do
  {
    usb_hal_state = hal_usb_get_state();
  }
  while(usb_hal_state != CONFIGURED);
}

void usb_send_packet(uint8_t* in_data, uint8_t ep_num, uint8_t size)
{
  if(ep_num == USB_EP_MOUSE)
  {
    while(!ep1_sent)
    ;
    ep1_sent = false;
  }
  else
  if(ep_num == USB_EP_KEYBOARD)
  {
    while(!ep2_sent)
    ;
    ep2_sent = false;
  }

  hal_usb_send_data(0x80 | ep_num, in_data, size);
}

static hal_usb_dev_req_resp_t device_req_cb(hal_usb_device_req* req, uint8_t** data_ptr, uint16_t* size) reentrant
{
    hal_usb_dev_req_resp_t retval;

    if( hal_usb_hid_device_req_proc(req, data_ptr, size, &retval) == true ) 
    {
        // The request was processed with the result stored in the retval variable
        return retval;
    }
    else
    {
        // The request was *not* processed by the HID subsystem
        return STALL;
    }

    return STALL;
}

static void suspend_cb(uint8_t allow_remote_wu) reentrant
{
  USBSLP = 1; // Disable USB clock (auto clear)

  if (allow_remote_wu == 1)
  {
    // Enable wake-up on USB and USBWU (bit3:0=1010)
    // Enable MCU_WU (bit5:4=10 ) on RTC
    WUCONF = (BIT_5 | BIT_3 | BIT_1);
    usb_state = USB_REM_WU_ENABLE;
  }
  else
  {
    // Enable wake-up on USB and USBWU (bit3:0=1010)
    WUCONF = (BIT_3 | BIT_1);
    usb_state = USB_REM_WU_DISABLE;
  }
}

static void resume_cb() reentrant
{
  ep1_sent = true;
  ep2_sent = true;
  usb_state = USB_AWAKE;
}

static void reset_cb() reentrant
{
  ep1_sent = true;
  ep2_sent = true;
  usb_state = USB_AWAKE; 
}

static uint8_t ep_1_in_cb(uint8_t *adr_ptr, uint8_t* size) reentrant
{
  ep1_sent = true;
  return 0x60; // NAK
}

static uint8_t ep_2_in_cb(uint8_t *adr_ptr, uint8_t* size) reentrant
{
  ep2_sent = true;
  return 0x60; // NAK
}

/** @} */
